package com.cy.ppj.comm.third.xy;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class ClientHttpRequestFactoryCreator {

    private String certFilePath;
    private String keyFilePath;

    private SSLContext context;

    public ClientHttpRequestFactoryCreator(String certFilePath, String keyFilePath) {
        this.certFilePath= certFilePath;
        this.keyFilePath= keyFilePath;
        try {
            this.context = SSLContextBuilder.create().build();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }

    private SSLSocketFactory sslSocketFactory() throws Exception {
        byte[] certBytes = FileUtils.readFileToByteArray(new File(certFilePath));
        byte[] keyBytes = FileUtils.readFileToByteArray(new File(keyFilePath));
        certBytes = parseDERFromPEM(certBytes, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
        X509Certificate cert = generateCertificateFromDER(certBytes);
        PrivateKey key = generatePrivateKey(keyBytes);
        KeyStore keystore = KeyStore.getInstance("JKS");
        keystore.load(null);
        keystore.setCertificateEntry("cert-alias", cert);
        keystore.setKeyEntry("key-alias", key, "changeit".toCharArray(), new Certificate[] { cert });
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(keystore, "changeit".toCharArray());
        KeyManager[] km = kmf.getKeyManagers();
        context.init(km, null, null);
        return context.getSocketFactory();
    }

    private byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
        String data = new String(pem);
        if (data.indexOf(beginDelimiter) >= 0) {
            String[] tokens = data.split(beginDelimiter);
            tokens = tokens[1].split(endDelimiter);
            data = tokens[0];
        }
        return DatatypeConverter.parseBase64Binary(data);
    }

    public static PrivateKey generatePrivateKey2(byte[] keyByteArray) throws Exception {byte[] keyBytes;
        keyBytes = (new Base64()).decode(keyByteArray);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey publicKey = keyFactory.generatePrivate(keySpec);
        return publicKey;
    }

    private static PrivateKey generatePrivateKey(byte[] keyByteArray) throws IOException {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyByteArray);
        try {
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return  kf.generatePrivate(spec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
        CertificateFactory factory = CertificateFactory.getInstance("X.509");
        return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(certBytes));
    }


    private HttpClient httpClient() throws Exception {
        return HttpClientBuilder.create().setSslcontext(SSLContextBuilder.create().build())
                .setSSLSocketFactory(new SSLConnectionSocketFactory(sslSocketFactory(), new AllowAllHostnameVerifier()))
                .build();
    }

    public ClientHttpRequestFactory httpClientRequestFactory() throws Exception {
        return new HttpComponentsClientHttpRequestFactory(httpClient());
    }

}

